home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / Peter Lewis (TCPExample) / PNL Libraries / MyConnections.p < prev    next >
Encoding:
Text File  |  1996-01-17  |  18.7 KB  |  797 lines  |  [TEXT/CWIE]

  1. unit MyConnections;
  2.  
  3. { MyConnections © Peter N Lewis, 1993-95 }
  4.  
  5. interface
  6.  
  7.     uses
  8.         Types, TCPTypes, MyTypes, OpenTransport, MyTransport;
  9.  
  10.     const
  11.         tooManyConnections = -23099;
  12.         timeoutError = -23098;
  13.         failedToOpenError = -23097;
  14.  
  15. { Sequence: }
  16. { new(obj) }
  17. { oe:=obj.Create }
  18. { if oe=noErr then begin }
  19. {   do stuff}
  20. { end; }
  21. { obj.timetodie := true } { Don't call Destroy yourself }
  22.  
  23.     type
  24.         ConnectionBaseObject = object
  25.                 timetodie: boolean; { Set this to have Destroy called at the end of HandleConnection }
  26.                 connection_index: integer; { private! }
  27.                 closedone: boolean;
  28.                 heartbeat_period: longInt; { set to <=0 to disable heartbeats }
  29.                 heartbeat_time: longInt; { set to time of next heartbeat, it is automatically incrememnted by the period }
  30. { To enable heartbeats, set heartbeat_time to TickCount, and heartbeat_period to the period in ticks }
  31.                 timeout_time: longInt; { set to time to timeout TickCount }
  32.                 dnr_token: ptr;
  33.                 function Create: OSErr;
  34.                 procedure Destroy;
  35.                 procedure HeartBeat;
  36.                 procedure Failed (oe: OSErr);
  37.                 procedure Close;
  38.                 procedure HandleConnection;
  39.                 procedure SetHeartBeat(period: longint);
  40.             end;
  41.         NameSearchObject = object(ConnectionBaseObject)
  42.                 ip: longInt;
  43.                 procedure HandleConnection;
  44.                 override;
  45.                 procedure FindName (hostIP: longInt);
  46.                 procedure FoundName (name: Str255; error: OSErr);
  47.             end;
  48.         AddressSearchObject = object(ConnectionBaseObject)
  49.                 object_host: Str255;
  50.                 procedure HandleConnection;
  51.                 override;
  52.                 procedure FindAddress (hostName: Str255);
  53.                 procedure FoundAddress (ip: longInt);
  54.             end;
  55.         ListenerObject = object(ConnectionBaseObject)
  56.                 listener: Ptr;
  57.                 localport: integer;
  58.                 function Create: OSErr;
  59.                 override;
  60.                 procedure Destroy;
  61.                 override;
  62.                 function CreateListener(buffersize:longint; port:integer; listeners:integer): OSErr;
  63.                 procedure HandleConnection;
  64.                 override;
  65.                 procedure ConnectionAvailable( connection: TransportRef ); { override this - do not call it! }
  66.             end;
  67.         UDPObject = object(ConnectionBaseObject)
  68.                 tref: TransportUDPRef;
  69.                 localport: integer;
  70.                 function Create: OSErr;
  71.                 override;
  72.                 function CreatePort (buffersize: longInt; port: integer): OSErr;
  73.                 procedure Close;
  74.                 override;
  75.                 procedure Destroy;
  76.                 override;
  77.                 procedure HandleConnection;
  78.                 override;
  79.                 procedure PacketAvailable (remoteIP: longInt; remoteport: integer; datap: ptr; datalen: integer);
  80.                 procedure PacketsAvailable (count: integer);
  81.                 function SendPacket (remoteIP: longInt; remoteport: integer; datap: ptr; datalen: integer; checksum: boolean): OSErr;
  82.             end;
  83.         statusType = (CS_None, CS_Opening, CS_Established, CS_Closing);
  84.         ConnectionObject = object(ConnectionBaseObject)
  85.                 tref: TransportRef;
  86.                 status: statusType;
  87.                 ourport: integer;
  88.                 input_buffer: Handle;
  89.                 output_buffer: Handle;
  90.                 transfer_error:OSStatus;
  91.                 do_send_close: Boolean;
  92.                 function Create: OSErr;
  93.                 override;
  94.                 procedure Destroy;
  95.                 override;
  96.                 procedure HandleConnection;
  97.                 override;
  98.                 procedure NewConnection (actve: boolean; buffersize: longInt; localport: integer; remotehost: Str255);
  99.                 procedure NewPassiveConnection (buffersize: longInt; localport: integer);
  100.                 procedure NewActiveConnection (buffersize: longInt; remotehost: Str255);
  101.                 procedure NewExistingConnection(newtref: TransportRef);
  102.                 procedure Close;
  103.                 override;
  104.                 procedure BeginConnection; { override these }
  105.                 procedure Established;
  106.                 procedure Closing;
  107.                 procedure CharsAvailable;
  108.                 procedure DoTransfer;
  109.                 procedure SendString (s: Str255);
  110.                 procedure SendData(datap: ptr; len: longint);
  111.             end;
  112.         LineConnectionObject = object(ConnectionObject)
  113.                 crlf: CRLFTypes;
  114.                 last_check: longInt; { last input_buffer size, dont recheck unless it changes }
  115.                 function Create: OSErr;
  116.                 override;
  117.                 procedure CharsAvailable;
  118.                 override;
  119.                 procedure SendLine (s: Str255);
  120.                 procedure LineAvailable (line: Str255);
  121.                 procedure CheckLineAvailable; { You can override this and use input_buffer yourself }
  122.             end;
  123.  
  124.     procedure StartupConnections;
  125.  
  126. implementation
  127.  
  128.     uses
  129.         Devices, TextUtils, Memory, Events,
  130.         QLowLevel, 
  131.         DNR, MyStrings, MyMemory, MyMathUtils, TCPUtils, MyStartup;
  132.  
  133.     const
  134.         TCPCMagic = 'TCPC';
  135.         TCPCBadMagic = 'badc';
  136.  
  137.     const  { Tuning parameters }
  138.         connections_max = 128;
  139.         TO_FindAddress = 40 * 60;
  140.         TO_FindName = 40 * 60;
  141.         TO_ActiveOpen = 20 * 60;
  142.         TO_Closing = longInt(2) * 60 * 60;
  143.         TO_PassiveOpen = longInt(1) * 365 * 24 * 3600 * 60;  { One years should be safe enough right? :-) }
  144.  
  145.     type
  146.         myHostInfo = record
  147.                 hi: hostInfo;
  148.                 done: signedByte;
  149.             end;
  150.         myHIP = ^myHostInfo;
  151.  
  152.     var
  153.         max_connections: integer;
  154.         connections: array[1..connections_max] of ConnectionBaseObject;
  155.         quiting: boolean;
  156.  
  157.     function ConnectionBaseObject.Create: OSErr;
  158.         var
  159.             i: integer;
  160.             err: OSStatus;
  161.     begin
  162.         MoveHHi(handle(self));
  163.         HLock(handle(self));
  164.         dnr_token := nil;
  165.         err := noErr;
  166.         if quiting then begin
  167.             err := -12;
  168.         end;
  169.         if err = noErr then begin
  170.             err := OpenTransportSystem;
  171.         end;
  172.         if err = noErr then begin
  173.             i := 1;
  174.             while (i <= connections_max) & (connections[i] <> nil) do begin
  175.                 i := i + 1;
  176.             end;
  177.             if i <= connections_max then begin
  178.                 timetodie := false;
  179.                 connection_index := i;
  180.                 max_connections := Max( max_connections, i );
  181.                 connections[i] := self;
  182.                 heartbeat_period := -1;
  183.                 heartbeat_time := 0;
  184.                 timeout_time := maxLongInt;
  185.                 closedone := false;
  186.             end else begin
  187.                 connection_index := -1;
  188.                 err := tooManyConnections;
  189.             end;
  190.         end;
  191.         Create := err;
  192.     end;
  193.  
  194.     procedure ConnectionBaseObject.Destroy;
  195.     begin
  196.         if connection_index > 0 then begin
  197.             connections[connection_index] := nil;
  198.         end;
  199.         TransportAbortDNR(dnr_token);
  200.         dispose(self);
  201.     end;
  202.  
  203.     procedure ConnectionBaseObject.HeartBeat;
  204.     begin
  205.     end;
  206.  
  207.     procedure ConnectionBaseObject.Failed (err: OSErr);
  208.     begin
  209.         err := err; { UNUSED! }
  210.         timetodie := true;
  211.     end;
  212.  
  213.     procedure ConnectionBaseObject.Close;
  214.     begin
  215.         closedone := true;
  216.     end;
  217.  
  218.     procedure ConnectionBaseObject.SetHeartBeat(period: longint);
  219.         var
  220.             time: longint;
  221.     begin
  222.         time := TickCount;
  223.         if (heartbeat_period <= 0) or (period < 0) then begin
  224.             heartbeat_time := time;
  225.         end;
  226.         heartbeat_period := period;
  227.         if heartbeat_time < time then begin
  228.             heartbeat_time := time;
  229.         end;
  230.         if (heartbeat_period > 0) & (heartbeat_time > time + heartbeat_period) then begin
  231.             heartbeat_time := time + heartbeat_period;
  232.         end;
  233.     end;
  234.  
  235.     procedure ConnectionBaseObject.HandleConnection;
  236.         var
  237.             now: longInt;
  238.     begin
  239.         now := TickCount;
  240.         if now > timeout_time then begin
  241.             timeout_time := maxLongInt;
  242.             Failed(timeoutError);
  243.         end else if (heartbeat_period > 0) & (now >= heartbeat_time) then begin
  244.             HeartBeat;
  245.             heartbeat_time := heartbeat_time + heartbeat_period;
  246.             if heartbeat_time < now then begin
  247.                 heartbeat_time := now;
  248.             end;
  249.         end;
  250.     end;
  251.  
  252.     procedure AddressSearchObject.FindAddress (hostName: Str255);
  253.         var
  254.             err: OSErr;
  255.     begin
  256.         err := Create;
  257.         if err = noErr then begin
  258.             object_host := hostName;
  259.             err := TransportNameToAddr(hostName, dnr_token);
  260.             timeout_time := TickCount + TO_FindAddress;
  261.         end;
  262.         if err <> noErr then begin
  263.             Failed(err);
  264.             timetodie := true;
  265.         end;
  266.     end;
  267.  
  268.     procedure AddressSearchObject.FoundAddress (ip: longInt);
  269.     begin
  270.         ip := ip; { UNUSED! }
  271.     end;
  272.  
  273.     procedure AddressSearchObject.HandleConnection;
  274.         var
  275.             result: OSStatus;
  276.             addr:IPAddr;
  277.     begin
  278.         inherited HandleConnection;
  279.         if not timetodie then begin
  280.             TransportGetNameToAddrResult(dnr_token, result, nil, @addr, 1);
  281.             if result = noErr then begin
  282.                 FoundAddress(addr);
  283.                 timetodie := true;
  284.             end else if result <> inProgress then begin
  285.                 Failed(result);
  286.                 timetodie := true;
  287.             end;
  288.         end;
  289.     end;
  290.  
  291.     procedure NameSearchObject.FindName (hostIP: longInt);
  292.         var
  293.             err: OSErr;
  294.     begin
  295.         ip := hostIP;
  296.         err := Create;
  297.         if err = noErr then begin
  298.             err := TransportAddrToName(hostIP, dnr_token);
  299.             timeout_time := TickCount + TO_FindName;
  300.         end;
  301.         if err <> noErr then begin
  302.             Failed(err);
  303.             timetodie := true;
  304.         end;
  305.     end;
  306.  
  307.     procedure NameSearchObject.FoundName (name: Str255; error: OSErr);
  308.     begin
  309.         name := name; { UNUSED! }
  310.         error := error; { UNUSED! }
  311.     end;
  312.  
  313.     procedure NameSearchObject.HandleConnection;
  314.         var
  315.             result: OSStatus;
  316.             name:Str255;
  317.     begin
  318.         inherited HandleConnection;
  319.         if not timetodie then begin
  320.             TransportGetAddrToNameResult(dnr_token, result, name);
  321.             if result <> inProgress then begin
  322.                 FoundName(name, result);
  323.                 timetodie := true;
  324.             end;
  325.         end;
  326.     end;
  327.  
  328.     function ListenerObject.Create: OSErr;
  329.     begin
  330.         listener := nil;
  331.         localport := 0;
  332.         Create := inherited Create;
  333.     end;
  334.  
  335.     procedure ListenerObject.Destroy;
  336.     begin
  337.         if listener <> nil then begin
  338.             TransportDestroyListener( listener );
  339.         end;
  340.         inherited Destroy;
  341.     end;
  342.  
  343.     function ListenerObject.CreateListener(buffersize:longint; port:integer; listeners:integer): OSErr;
  344.         var
  345.             err: OSErr;
  346.     begin
  347.         err := Create;
  348.         if err = noErr then begin
  349.             localport := port;
  350.             err := TransportListen( listener, localport, listeners, buffersize);
  351.             timeout_time := maxLongInt;
  352.         end;
  353.         if err <> noErr then begin
  354.             timetodie := true;
  355.         end;
  356.         CreateListener := err;
  357.     end;
  358.     
  359.     procedure ListenerObject.ConnectionAvailable( connection: TransportRef );
  360.     begin
  361.         TransportDestroy( connection );
  362.     end;
  363.     
  364.     procedure ListenerObject.HandleConnection;
  365.         var
  366.             connection:TransportRef;
  367.     begin
  368.         if TransportGetListenerConnection( listener, connection ) = noErr then begin
  369.             ConnectionAvailable( connection );
  370.         end;
  371.         inherited HandleConnection;
  372.     end;
  373.     
  374.     function UDPObject.Create: OSErr;
  375.     begin
  376.         tref := nil;
  377.         localport := 0;
  378.         Create := inherited Create;
  379.     end;
  380.  
  381.     function UDPObject.CreatePort (buffersize: longInt; port: integer): OSErr;
  382.         var
  383.             err: OSErr;
  384.     begin
  385.         err := Create;
  386.         if err = noErr then begin
  387.             err := TransportUDPOpenPort(tref, port, buffersize);
  388.             localport := port;
  389.             timeout_time := maxLongInt;
  390.         end;
  391.         if err <> noErr then begin
  392.             timetodie := true;
  393.         end;
  394.         CreatePort := err;
  395.     end;
  396.  
  397.     procedure UDPObject.Close;
  398.     begin
  399.         timetodie := true;
  400.         inherited Close;
  401.     end;
  402.  
  403.     procedure UDPObject.Destroy;
  404.     begin
  405.         TransportUDPDestroy(tref);
  406.         inherited Destroy;
  407.     end;
  408.  
  409.     procedure UDPObject.PacketAvailable (remoteIP: longInt; remoteport: integer; datap: ptr; datalen: integer);
  410.     begin
  411.         remoteIP := remoteIP; { UNUSED! }
  412.         remoteport := remoteport; { UNUSED! }
  413.         datap := datap; { UNUSED! }
  414.         datalen := datalen; { UNUSED! }
  415.     end;
  416.  
  417.     procedure UDPObject.PacketsAvailable (count: integer);
  418.         var
  419.             err: OSErr;
  420.             remoteIP: longInt;
  421.             remoteport: integer;
  422.             datap: ptr;
  423.             datalen: integer;
  424.     begin
  425.         count := count; { UNUSED! }
  426.         err := TransportUDPRead (tref, remoteIP, remoteport, datap, datalen);
  427.         if err = noErr then begin
  428.             PacketAvailable(remoteIP, remoteport, datap, datalen);
  429.             err := TransportUDPReturnBuffer(tref, datap);
  430.         end;
  431.     end;
  432.  
  433.     function UDPObject.SendPacket (remoteIP: longInt; remoteport: integer; datap: ptr; datalen: integer; checksum: boolean): OSErr;
  434.     begin
  435.         SendPacket := TransportUDPWrite (tref, remoteIP, remoteport, datap, datalen, checksum);
  436.     end;
  437.  
  438.     procedure UDPObject.HandleConnection;
  439.         var
  440.             count: longInt;
  441.     begin
  442.         inherited HandleConnection;
  443.         if not timetodie & (tref <> nil) then begin
  444.             count := TransportUDPDatagramsAvailable(tref);
  445.             if count > 0 then begin
  446.                 PacketsAvailable(count);
  447.             end;
  448.         end;
  449.     end;
  450.  
  451.     procedure ConnectionObject.Established;
  452.     begin
  453.     end;
  454.  
  455.     procedure ConnectionObject.Closing;
  456.     begin
  457.         Close;
  458.     end;
  459.  
  460.     procedure ConnectionObject.CharsAvailable;
  461.     begin
  462.     end;
  463.  
  464.     function ConnectionObject.Create: OSErr;
  465.         var
  466.             err, err2:OSErr;
  467.     begin
  468.         err := inherited Create;
  469.         status := CS_None;
  470.         transfer_error := noErr;
  471.         do_send_close := false;
  472.         err2 := MNewHandle(input_buffer, 0);
  473.         if err = noErr then begin
  474.             err := err2;
  475.         end;
  476.         err2 := MNewHandle(output_buffer, 0);
  477.         if err = noErr then begin
  478.             err := err2;
  479.         end;
  480.         Create := err;
  481.     end;
  482.     
  483.     procedure ConnectionObject.Destroy;
  484.     begin
  485.         TransportDestroy(tref);
  486.         MDisposeHandle(input_buffer);
  487.         MDisposeHandle(output_buffer);
  488.         inherited Destroy;
  489.     end;
  490.  
  491.     procedure ConnectionObject.SendData(datap: ptr; len: longint);
  492.         var
  493.             err: OSErr;
  494.     begin
  495.         if ((status = CS_Established) or (status = CS_Closing)) and not closedone then begin
  496.             err := PtrAndHand(datap, output_buffer, len);
  497.         end else begin
  498.             err := -24;
  499.         end;
  500.         if transfer_error = noErr then begin
  501.             transfer_error := err;
  502.         end;
  503.     end;
  504.  
  505.     procedure ConnectionObject.SendString (s: Str255);
  506.     begin
  507.         SendData(@s[1], length(s));
  508.     end;
  509.  
  510.     procedure ConnectionObject.DoTransfer;
  511.         procedure SetErr(err:OSStatus);
  512.         begin
  513.             if (transfer_error = noErr) then begin
  514.                 transfer_error := err;
  515.             end;
  516.         end;
  517.         var
  518.             err: OSStatus;
  519.             count, len:longint;
  520.     begin
  521.         len := GetHandleSize(input_buffer);
  522.         count := Min(TransportCharsAvailable(tref), 10240-len);
  523.         if count > 0 then begin
  524.             err := MSetHandleSize(input_buffer, len + count);
  525.             if err = noErr then begin
  526.                 HLock(input_buffer);
  527.                 err := TransportReceive(tref, AddPtrLong(input_buffer^, len), count, count);
  528.                 HUnlock(input_buffer);
  529.                 SetErr(err);
  530.                 SetHandleSize(input_buffer, len + count);
  531.             end;
  532.         end;
  533.  
  534.         len := GetHandleSize(output_buffer);
  535.         if len > 0 then begin
  536.             HLock(output_buffer);
  537.             err := TransportSend(tref, output_buffer^, len);
  538.             HUnlock(output_buffer);
  539.             SetHandleSize(output_buffer, 0);
  540.             SetErr(err);
  541.         end else if do_send_close then begin
  542.             do_send_close := false;
  543.             TransportSendClose(tref);
  544.         end;
  545.     end;
  546.     
  547.     procedure ConnectionObject.BeginConnection;
  548.     begin
  549.     end;
  550.  
  551.     procedure ConnectionObject.NewExistingConnection(newtref: TransportRef);
  552.         var
  553.             err: OSStatus;
  554.     begin
  555.         tref := newtref;
  556.         err := Create;
  557.         if err = noErr then begin
  558.             err := TransportHandleTransfers(tref);
  559.         end;
  560.         if err = noErr then begin
  561.             status := CS_Established;
  562.             ourport := 0;
  563.             timeout_time := maxLongInt;
  564.             BeginConnection;
  565.             Established;
  566.         end else begin
  567.             Failed(err);
  568.         end;
  569.     end;
  570.     
  571.     procedure ConnectionObject.NewConnection (active: boolean; buffersize: longInt; localport: integer; remotehost: Str255);
  572.         var
  573.             err: OSErr;
  574.     begin
  575.         tref := nil;
  576.         err := Create;
  577.         if err = noErr then begin
  578.             status := CS_Opening;
  579.             ourport := localport;
  580.             if active then begin
  581.                 err := TransportOpenActiveConnection(tref, remotehost, ourport, buffersize);
  582.                 timeout_time := TickCount + TO_ActiveOpen;
  583.             end else begin
  584.                 err := TransportOpenPassiveConnection(tref, ourport, buffersize);
  585.                 timeout_time := TickCount + TO_PassiveOpen;
  586.             end;
  587.         end;
  588.         if err = noErr then begin
  589.             err := TransportHandleTransfers(tref);
  590.         end;
  591.         if err = noErr then begin
  592.             BeginConnection;
  593.         end else begin
  594.             Failed(err);
  595.             timetodie := true;
  596.         end;
  597.     end;
  598.  
  599.     procedure ConnectionObject.NewPassiveConnection (buffersize: longInt; localport: integer);
  600.     begin
  601.         NewConnection(false, buffersize, localport, '');
  602.     end;
  603.  
  604.     procedure ConnectionObject.NewActiveConnection (buffersize: longInt; remotehost: Str255);
  605.     begin
  606.         NewConnection(true, buffersize, 0, remotehost);
  607.     end;
  608.  
  609.     procedure ConnectionObject.Close;
  610.     begin
  611.         if not closedone and (tref <> nil) then begin
  612.             if GetHandleSize(output_buffer) > 0 then begin
  613.                 do_send_close := true;
  614.             end else begin
  615.                 TransportSendClose(tref);
  616.             end;
  617.         end;
  618.         closedone := true;
  619.     end;
  620.  
  621.     procedure ConnectionObject.HandleConnection;
  622.         var
  623.             state: TCPStateType;
  624.             result: OSStatus;
  625.     begin
  626.         inherited HandleConnection;
  627.         if not timetodie then begin
  628.             case status of
  629.                 CS_Opening:  begin
  630.                     TransportGetOpenResult(tref, result);
  631.                     if result = noErr then begin
  632.                         Established;
  633.                         status := CS_Established;
  634.                         timeout_time := maxLongInt;
  635.                     end else if result <> inProgress then begin
  636.                         Failed(result);
  637.                         timetodie := true;
  638.                     end;
  639.                 end;
  640.                 CS_Established:  begin
  641.                     DoTransfer;
  642.                     state := TransportGetConnectionState(tref);
  643.                     case state of
  644.                         T_Established:  begin
  645.                             if GetHandleSize(input_buffer) > 0 then begin
  646.                                 CharsAvailable;
  647.                             end;
  648.                         end;
  649.                         T_PleaseClose, T_Closing:  begin
  650.                             if GetHandleSize(input_buffer) > 0 then begin
  651.                                 CharsAvailable;
  652.                             end else begin
  653.                                 Closing;
  654.                                 status := CS_Closing;
  655.                                 timeout_time := TickCount + TO_Closing;
  656.                             end;
  657.                         end;
  658.                         T_Dead, T_Bored:  begin
  659.                             Closing;
  660.                             status := CS_Closing;
  661.                             timeout_time := TickCount + TO_Closing;
  662.                         end;
  663.                         otherwise
  664.                             ;
  665.                     end;
  666.                 end;
  667.                 CS_Closing:  begin
  668.                     DoTransfer;
  669.                     state := TransportGetConnectionState(tref);
  670.                     case state of
  671.                         T_PleaseClose, T_Closing, T_Established:  begin
  672.                             if GetHandleSize(input_buffer) > 0 then begin
  673.                                 CharsAvailable;
  674.                             end;
  675.                         end;
  676.                         T_Dead, T_Bored:  begin
  677.                             timetodie := true;
  678.                         end;
  679.                         otherwise
  680.                             ;
  681.                     end;
  682.                 end;
  683.                 otherwise
  684.                     ;
  685.             end;
  686.         end;
  687.     end;
  688.  
  689.     function LineConnectionObject.Create: OSErr;
  690.     begin
  691.         crlf := CL_CRLF;
  692.         last_check := -1;
  693.         Create := inherited Create;
  694.     end;
  695.  
  696.     procedure LineConnectionObject.SendLine (s: Str255);
  697.     begin
  698.         if crlf <> CL_LF then begin
  699.             s := concat(s, cr);
  700.         end;
  701.         if crlf <> CL_CR then begin
  702.             s := concat(s, lf);
  703.         end;
  704.         SendData(@s[1], length(s));
  705.     end;
  706.  
  707.     procedure LineConnectionObject.LineAvailable (line: Str255);
  708.     begin
  709.         line := line; { UNUSED! }
  710.     end;
  711.  
  712.     procedure LineConnectionObject.CharsAvailable;
  713.     begin
  714.         CheckLineAvailable;
  715.     end;
  716.  
  717.     procedure LineConnectionObject.CheckLineAvailable;
  718.         var
  719.             len, inbuf: longInt;
  720.             p: ptr;
  721.             s: Str255;
  722.     begin
  723.         while true do begin
  724.             inbuf := GetHandleSize(input_buffer);
  725.             if (inbuf = 0) | (inbuf = last_check) then begin
  726.                 leave;
  727.             end;
  728.             p := input_buffer^;
  729.             len := 0;
  730.             while (len < inbuf) & (len < 255) & (p^ <> ord(lf)) & (p^ <> ord(cr)) do begin
  731.                 p := ptr(ord(p) + 1);
  732.                 len := len + 1;
  733.             end;
  734.             if (len = 255) | ((len < inbuf) & ((p^ = ord(lf)) | (p^ = ord(cr)))) then begin
  735. {$PUSH}
  736. {$R-}
  737.                 s[0] := chr(len);
  738.                 BlockMoveData(input_buffer^, @s[1], len);
  739. {$POP}
  740.                 if (len < inbuf) & (p^ = ord(cr)) then begin
  741.                     p := ptr(ord(p) + 1);
  742.                     len := len + 1;
  743.                 end;
  744.                 if (len < inbuf) & (p^ = ord(lf)) then begin
  745.                     p := ptr(ord(p) + 1);
  746.                     len := len + 1;
  747.                 end;
  748.                 MMungerDelete(input_buffer, 0, len);
  749.                 LineAvailable(s);
  750.                 last_check := -1;
  751.             end else begin
  752.                 last_check := inbuf;
  753.             end;
  754.         end;
  755.     end;
  756.  
  757.     procedure IdleConnections;
  758.         var
  759.             i: integer;
  760.     begin
  761.         for i := 1 to max_connections do begin
  762.             if connections[i] <> nil then begin
  763.                 if not connections[i].timetodie then begin
  764.                     connections[i].HandleConnection;
  765.                 end;
  766.                 if connections[i].timetodie then begin
  767.                     connections[i].Destroy;
  768.                 end;
  769.             end;
  770.         end;
  771.     end;
  772.  
  773.     procedure FinishConnections;
  774.         var
  775.             i: integer;
  776.     begin
  777.         for i := 1 to max_connections do begin
  778.             if connections[i] <> nil then begin
  779.                 connections[i].Destroy;
  780.             end;
  781.         end;
  782.     end;
  783.  
  784.     procedure StartupConnections;
  785.         var
  786.             i: integer;
  787.     begin
  788.         quiting := false;
  789.         for i := 1 to connections_max do begin
  790.             connections[i] := nil;
  791.         end;
  792.         max_connections := 0;
  793.         StartupTransport;
  794.         SetStartup(nil, IdleConnections, 0, FinishConnections);
  795.     end;
  796.  
  797. end.